﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace CQLQueries.Analyzers
{
    public class CQL_LA
    {
        // шаблон "одиночка"
        private static CQL_LA instance;

        /// <summary>
        /// Единственный экземпляр класса контроллера
        /// </summary>
        public static CQL_LA Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new CQL_LA();
                }
                return instance;
            }
        }

        public CQLTokens CurTok;//Текущий токен        
        public string CurText;//Текущее значение текста
        public int CurPos;//Текущая позиция символа

        public int BeginToken;
        public int EndToken;
        public int EndQuery;

        public event EventHandler<ErrorArgs> ErrorOccuredEvent;
        
        private void ErrorLexical(String message)
        {
            if (ErrorOccuredEvent != null)
                ErrorOccuredEvent(this, new ErrorArgs(message,
                    CQL_LA.Instance.CurPos - CQL_LA.Instance.CurText.Length,
                    "Лексическая ошибка", CQL_LA.Instance.BeginToken, CQL_LA.Instance.EndToken));
        }
        
        public bool Scan(int index, string QueryStr)
        {
            CurPos = index;
            int State = 0;
            CurText = "";
            String PrevText = "";
            CurTok = CQLTokens.Undef;
            CQLTokens TempTok = CQLTokens.Undef;
            while(CurPos < QueryStr.Length)
            {
                char c = QueryStr[CurPos];
                CurText += c;
                CurPos++;
                switch (State)
                { 
                    case 0:
                        switch (c)
                        {
                            case '(':
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.LBr;
                                return true;
                            case ')':
                                EndQuery = CurPos;
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.RBr;
                                return true;
                            case '=':
                                BeginToken = CurPos;
                                CurTok = CQLTokens.Equal;
                                State = 10;
                                break;
                            case '!':
                                BeginToken = CurPos;
                                CurTok = CQLTokens.NotEqual;
                                State = 10;
                                break;
                            case '>':
                                BeginToken = CurPos;
                                CurTok = CQLTokens.More;
                                TempTok = CQLTokens.MoreOrEqual;
                                State = 20;
                                break;
                            case '<':
                                BeginToken = CurPos;
                                CurTok = CQLTokens.Less;
                                TempTok = CQLTokens.LessOrEqual;
                                State = 20;
                                break;
                            case 'S':
                                BeginToken = CurPos;
                                State = 100;
                                break;
                            case 'W':
                                BeginToken = CurPos;
                                State = 200;
                                break;
                            case 'L':
                                BeginToken = CurPos;
                                State = 250;
                                break;
                            case 'N':
                                BeginToken = CurPos;
                                State = 260;
                                break;
                            case 'O':
                                BeginToken = CurPos;
                                State = 600;
                                break;
                            case 'A':
                                BeginToken = CurPos;
                                State = 601;
                                break;
                            case 'D':
                                BeginToken = CurPos;
                                State = 700;
                                break;
                            case '"':
                                BeginToken = CurPos;
                                State = 301;
                                break;
                            case '+':
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.Add;
                                return true;
                            case '-':
                                BeginToken = CurPos;
                                State = 5;
                                break;
                            case '/':
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.Div;
                                return true;
                            case '*':
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.Mul;
                                return true;
                            case '%':
                                BeginToken = CurPos;
                                EndToken = CurPos;
                                CurTok = CQLTokens.Mod;
                                return true;
                            default:
                                if (char.IsDigit(c))
                                {
                                    BeginToken = CurPos;
                                    CurTok = CQLTokens.Const;
                                    State = 300;
                                }
                                else if (char.IsLetter(c))
                                {
                                    BeginToken = CurPos;
                                    CurTok = CQLTokens.Id;
                                    State = 30;
                                }
                                else
                                {
                                    if (!(c == ' ') && !(c == '\n'))
                                    {
                                        ErrorLexical("Запрос содержит не допустимый символ.");
                                        return false;
                                    }
                                    CurText = PrevText;
                                }
                                break;
                        }
                        break;
                    case 5:
                        if (c == '-')
                        {
                            State = 500;
                        }
                        else
                        {
                            EndToken = CurPos;
                            CurTok = CQLTokens.Sub;
                            CurPos--;
                            CurText = PrevText;
                            return true;
                        }
                        break;
                    case 10:
                        if(c=='=')
                        {
                            return true;
                        }
                        else
                        {
                            EndToken = CurPos;
                            ErrorLexical("Неожиданная конструкция языка.");
                            return false;
                        }
                    case 20:
                        if (c == '=')
                        {
                            CurTok = TempTok;
                        }
                        else
                        {
                            CurPos--;
                            CurText = PrevText;
                        }
                        return true;
                    case 30:
                        if (!char.IsLetter(c) && !char.IsDigit(c) && !(c == '_') && 
                            !(c == '(') && !(c == ')') &&
                            !(c == '[') && !(c == ']') && 
                            !(c == '.'))
                        {
                            EndToken = CurPos;
                            EndQuery = CurPos;
                            CurTok = CQLTokens.Id;
                            CurPos--;
                            CurText = PrevText;
                            return true;
                        }
                        break;
                    case 100:
                        if (c=='E')
                        {
                            State = 101;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 101:
                        if (c=='L')
                        {
                            State = 102;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 102:
                        if (c=='E')
                        {
                            State = 103;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 103:
                        if (c=='C')
                        {
                            State = 104;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 104:
                        if (c=='T')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Select;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 200:
                        if (c=='H')
                        {
                            State = 201;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 201:
                        if (c == 'E')
                        {
                            State = 202;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 202:
                        if (c == 'R')
                        {
                            State = 203;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 203:
                        if (c == 'E')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Where;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 250:
                        if (c == 'I')
                        {
                            State = 251;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 251:
                        if (c == 'K')
                        {
                            State = 252;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 252:
                        if (c == 'E')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Like;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 260:
                        if (c == 'O')
                        {
                            State = 261;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 261:
                        if (c == 'T')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Not;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 300:
                        if (!char.IsDigit(c))
                        {
                            CurPos--;
                            CurText = PrevText;
                            EndToken = CurPos;
                            EndQuery = CurPos;
                            CurTok = CQLTokens.Const;                            
                            return true;
                        }
                        break;
                    case 301:
                        if (c == '"')
                        {
                            EndToken = CurPos;
                            EndQuery = CurPos;
                            CurTok = CQLTokens.Const;
                            return true;
                        }
                        //else if (!char.IsLetter(c) && !char.IsDigit(c) && !(c == '_') && !(c == '$') && !(c == '^') && !(c == '*') && !(c == '?') && !(c == '.') && !(c == '|') && !(c == '[') && !(c == ']') && !(c == '-'))
                        //{
                        //    CurPos--;
                        //    ErrorLexical("Неожиданная конструкция языка.");
                        //    return false;
                        //}
                        break;
                    case 400:
                        if ((c == ' ')||(c == '\n')||(c == '('))
                        {
                            CurPos--;
                            CurTok = TempTok;
                            return true;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 500:
                        if (c == '\n')
                        {
                            CurText = "";
                            State = 0;
                        }
                        break;
                    case 600:
                        if (c == 'R')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Or;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 601:
                        if (c == 'N')
                        {
                            State = 602;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 602:
                        if (c == 'D')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.And;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 700:
                        if (c == 'I')
                        {
                            State = 701;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 701:
                        if (c == 'S')
                        {
                            State = 702;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 702:
                        if (c == 'T')
                        {
                            State = 703;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 703:
                        if (c == 'I')
                        {
                            State = 704;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 704:
                        if (c == 'N')
                        {
                            State = 705;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 705:
                        if (c == 'C')
                        {
                            State = 706;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                    case 706:
                        if (c == 'T')
                        {
                            EndToken = CurPos;
                            TempTok = CQLTokens.Distinct;
                            State = 400;
                        }
                        else
                        {
                            State = 30;
                            CurPos--;
                            CurText = PrevText;
                        }
                        break;
                }
                PrevText = CurText;
            }
            return true;//В запросе найден токен
        }
    }
}
